/*
 * grafo.c
 *
 *  Created on: 04/09/2011
 *      Author: guido
 */

#include "grafo.h"
#include "lista.h"
#include "iterador.h"

void Grafo_init(Grafo* grafo) {
	grafo->usuarios = malloc(sizeof(Lista));
	Lista_init(grafo->usuarios);
}

/* Busca en la lista al usuario con numero especificado.
 * Si lo encuentra, devuelve un codigo de error.
 * Si no lo encuentra, aloca en heap una nueva instancia de Usuario y
 * lo agre a la lista */
int Grafo_agregarUsuario(Grafo* g, char* numero, char* apellido, char* nombre) {
	Iterador it = Lista_buscar(g->usuarios, numero);
	if (It_tieneSiguiente(&it))
		return COD_YA_EXISTE;
	Usuario* usuarioNuevo = malloc(sizeof(Usuario));
	Usuario_init(usuarioNuevo, numero, apellido, nombre);
	Lista_agregar(g->usuarios, usuarioNuevo);
	return COD_OK;
}

/* Busca el usuario con numero especificado
 * Si no lo encuentra, devuelve un codigo de error
 * Si lo encuentra, primero se borra el nodo de la lista de usuarios
 * del grafo que lo apuntaba, luego se borra dicho usuario, y borrando
 * las referencias de sus amigos a este.*/
int Grafo_removerUsuario(Grafo* g, char* numero) {
	Iterador it = Lista_buscar(g->usuarios, numero);
	Usuario* usuarioABorrar = It_remover(&it);
	if (usuarioABorrar == NULL)
		return COD_NO_EXISTE;
	else {
		Usuario_destroy(usuarioABorrar);
		free(usuarioABorrar);
		return COD_OK;
	}
}

/* Busca en el grafo ambos numeros. Si uno no se encuentra devuelve
 * codigo de error. Tambien devuelve codigo de error si ya son amigos
 * Si no, agrega la relación bidireccional entre los usuarios */
int Grafo_amigar(Grafo* g, char* numUsuario, char* numAmigo) {
	Iterador itUsuario = Lista_buscar(g->usuarios, numUsuario);
	Iterador itAmigo = Lista_buscar(g->usuarios, numAmigo);
	if (!It_tieneSiguiente(&itUsuario))
		return COD_NO_EXISTE;
	if (!It_tieneSiguiente(&itAmigo))
		return COD_AMIGO_NO_EXISTE;
	Usuario* usuario = It_get(&itUsuario);
	Usuario* amigo = It_get(&itAmigo);
	int codigo = 0;
	codigo = Usuario_agregarAmigo(usuario, amigo);
	if (codigo != 0)
		return codigo;
	codigo = Usuario_agregarAmigo(amigo, usuario);
	if (codigo != 0)
		return codigo;
	return COD_OK;
}

/* Busca en el grafo ambos numeros. Si uno no se encuentra devuelve
 * codigo de error. Tambien devuelve codigo de error si no son amigos
 * Si no, borra la relación bidireccional entre los usuarios */
int Grafo_desamigar(Grafo* g, char* numUsuario, char* numAmigo) {
	Iterador itUsuario = Lista_buscar(g->usuarios, numUsuario);
	Iterador itAmigo = Lista_buscar(g->usuarios, numAmigo);
	if (!It_tieneSiguiente(&itUsuario))
		return COD_NO_EXISTE;
	if (!It_tieneSiguiente(&itAmigo))
		return COD_AMIGO_NO_EXISTE;
	Usuario* usuario = It_get(&itUsuario);
	Usuario* amigo = It_get(&itAmigo);
	int codigo = 0;
	codigo = Usuario_borrarAmigo(usuario, amigo);
	if (codigo != 0)
		return codigo;
	codigo = Usuario_borrarAmigo(amigo, usuario);
	if (codigo != 0)
		return codigo;
	return COD_OK;
}

Usuario* Grafo_getUsuario(Grafo* g, char* numUsuario) {
	Iterador it = Lista_buscar(g->usuarios, numUsuario);
	if ( !It_tieneSiguiente(&it))
		return NULL;
	return (It_get(&it));
}

/* Almacena en lista los numeros de recomendación para el numero especificado
 * Devuelve codigo de error si no existe el usuario con el numero especificado
 * Para cada amigo, agrega a la lista su lista de amigos, chequeando con
 * usuario que no se agreguen ni el usuario especificado, ni sus amigos.
 * Luego se borran los duplicados */
int Grafo_listaDeAmigosPosibles(Grafo* g, char* numero, Lista* lista) {
	Iterador it = Lista_buscar(g->usuarios, numero);
	if ( !It_tieneSiguiente(&it))
		return COD_NO_EXISTE;
	Usuario* usuario = It_get(&it);
	Iterador itAmigos = Lista_iterador(usuario->amigos);
	while ( It_tieneSiguiente(&itAmigos) ) {
		Usuario* amigo = It_get(&itAmigos);
		Lista_agregarListaDePosibles(lista, amigo->amigos, usuario);
		It_siguiente(&itAmigos);
	}
	Lista_borrarDuplicados(lista);

	Iterador itResultado = Lista_iterador(lista);
	if ( !It_tieneSiguiente(&itResultado) ) {
		return COD_NO_HAY_RECOMENDACIONES;
	}
	return COD_OK;
}

/*Destruye el grafo, destruyendo la lista de usuarios dentro
 * y dichas instancias de usuario. */
void Grafo_destroy(Grafo* g) {
	Iterador it = Lista_iterador(g->usuarios);
	while (It_tieneSiguiente(&it)) {
		Usuario* usuario = It_siguiente(&it);
		Usuario_destroy(usuario);
		free(usuario);
	}
	Lista_destroy(g->usuarios);
	free(g->usuarios);
}
